#include "pch.h"
#include "D2DGraphics.h"
#include "Brushes.h"
#include "StrokeStyle.h"
#include "ErrorCodes.h"
#include "TextFormat.h"
#include "TextLayout.h"
#include "GlyphRun.h"
#include "Geometries.h"
#include "Image.h"

using namespace Platform;

using namespace DXBase;
using namespace DXBase::D2D;
using namespace DXBase::DWrite;


D2DGraphics::D2DGraphics(DXContext^ ctxt)
	: mContext(ctxt)
{
	if (!ctxt)
		throw ref new NullReferenceException();
}

void D2DGraphics::Clear(Windows::UI::Color bg)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	Context->m_d2dContext->Clear(DXBase::makeD2D(bg));
}
void D2DGraphics::Flush()
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	HR2Ex(Context->m_d2dContext->Flush());
}


float3x2 D2DGraphics::Transform::get()
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);

	D2D1_MATRIX_3X2_F m;
	Context->m_d2dContext->GetTransform(&m);

	float3x2 result;
	result.m11 = m._11;
	result.m12 = m._12;
	result.m21 = m._21;
	result.m22 = m._22;
	result.m31 = m._31;
	result.m32 = m._32;
	return result;
}
void D2DGraphics::Transform::set(float3x2 value)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	auto t = makeD2D(value);
	Context->m_d2dContext->SetTransform(&t);
}
UnitMode D2DGraphics::UnitMode::get()
{
	D2D1_UNIT_MODE m = Context->m_d2dContext->GetUnitMode();
	return fromD2D(m);
}
void D2DGraphics::UnitMode::set(D2D::UnitMode value)
{
	Context->m_d2dContext->SetUnitMode(makeD2D(value));
}

void D2DGraphics::DrawBitmap(Bitmap^ bmp, Windows::Foundation::Rect* rect, float opacity, INTERPOLATION_MODE im, Windows::Foundation::Rect* srcRect, float4x4* perspective)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!bmp)
		throw ref new NullReferenceException();

	bmp->Context = Context;
	bmp->LoadDXData();

	D2D1_RECT_F dest, *pdest;
	if (rect)
	{
		dest = DXBase::makeD2D(*rect);
		pdest = &dest;
	}
	else
	{
		pdest = nullptr;
	}
	D2D1_RECT_F src, *psrc;
	if (srcRect)
	{
		src = DXBase::makeD2D(*srcRect);
		psrc = &src;
	}
	else
	{
		psrc = nullptr;
	}
	D2D1_MATRIX_4X4_F tfm, *ptfm;
	if (perspective)
	{
		tfm = makeD2D(*perspective);
		ptfm = &tfm;
	}
	else
	{
		ptfm = nullptr;
	}

	Context->m_d2dContext->DrawBitmap(
		bmp->pBitmap.Get(), 
		pdest,
		opacity,
		static_cast<D2D1_INTERPOLATION_MODE>(im),
		psrc,
		ptfm);
}


void D2DGraphics::DrawLine(float2 p1, float2 p2, IBrush^ brush, float width, StrokeStyle^ stroke)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();

	brush->Context = Context;
	Context->m_d2dContext->DrawLine(
		makeD2D(p1), makeD2D(p2), 
		brush->GetNativeBrush()->brush.Get(), width, 
		stroke ? stroke->GetNativeStrokeStyle().Get() : nullptr);
}

void D2DGraphics::DrawRectangle(Windows::Foundation::Rect rect, IBrush^ brush, float strokeWidth, StrokeStyle^ stroke)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();

	brush->Context = Context;
	Context->m_d2dContext->DrawRectangle(
		DXBase::makeD2D(rect), 
		brush->GetNativeBrush()->brush.Get(), strokeWidth, 
		stroke ? stroke->GetNativeStrokeStyle().Get() : nullptr);
}
void D2DGraphics::FillRectangle(Windows::Foundation::Rect rect, IBrush^ brush)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();

	brush->Context = Context;

	Context->m_d2dContext->FillRectangle(DXBase::makeD2D(rect), brush->GetNativeBrush()->brush.Get());
}

void D2DGraphics::DrawEllipse(Windows::Foundation::Rect rect, IBrush^ brush, float strokeWidth, StrokeStyle^ stroke)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();

	brush->Context = Context;
	D2D1_ELLIPSE deps = { rect.X + rect.Width / 2, rect.Y + rect.Height / 2, rect.Width / 2, rect.Height / 2 };
	Context->m_d2dContext->DrawEllipse(
		deps, 
		brush->GetNativeBrush()->brush.Get(), strokeWidth, 
		stroke ? stroke->GetNativeStrokeStyle().Get() : nullptr);
}
void D2DGraphics::FillEllipse(Windows::Foundation::Rect rect, IBrush^ brush)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();

	brush->Context = Context;

	D2D1_ELLIPSE deps = { rect.X + rect.Width / 2, rect.Y + rect.Height / 2, rect.Width / 2, rect.Height / 2 };
	Context->m_d2dContext->FillEllipse(deps, brush->GetNativeBrush()->brush.Get());
}

void D2DGraphics::DrawRoundedRect(Windows::Foundation::Rect rect, float radiusX, float radiusY, IBrush^ brush, float strokeWidth, StrokeStyle^ stroke)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();

	brush->Context = Context;
	D2D1_ROUNDED_RECT drect = { rect.Left, rect.Top, rect.Right, rect.Bottom, radiusX, radiusY };
	Context->m_d2dContext->DrawRoundedRectangle(
		drect, 
		brush->GetNativeBrush()->brush.Get(), strokeWidth, 
		stroke ? stroke->GetNativeStrokeStyle().Get() : nullptr);
}
void D2DGraphics::FillRoundedRect(Windows::Foundation::Rect rect, float radiusX, float radiusY, IBrush^ brush)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();

	brush->Context = Context;

	D2D1_ROUNDED_RECT drect = { rect.Left, rect.Top, rect.Right, rect.Bottom, radiusX, radiusY };
	Context->m_d2dContext->FillRoundedRectangle(drect, brush->GetNativeBrush()->brush.Get());
}

void D2DGraphics::DrawGeometry(IGeometry^ geom, IBrush^ brush, float strokeWidth, StrokeStyle^ stroke)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();
	if (!geom)
		throw ref new NullReferenceException();

	auto pGeom = geom->GetNativeGeometry();
	if (!pGeom)
		return;

	brush->Context = Context;
	Context->m_d2dContext->DrawGeometry(
		pGeom->mGeom.Get(), 
		brush->GetNativeBrush()->brush.Get(), 
		strokeWidth, 
		stroke ? stroke->GetNativeStrokeStyle().Get() : nullptr);
}
void D2DGraphics::FillGeometry(IGeometry^ geom, IBrush^ brush, IBrush^ opacityBrush)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!brush)
		throw ref new NullReferenceException();
	if (!geom)
		throw ref new NullReferenceException();

	auto pGeom = geom->GetNativeGeometry();
	if (!pGeom)
		return;

	brush->Context = Context;
	Context->m_d2dContext->FillGeometry(
		pGeom->mGeom.Get(), 
		brush->GetNativeBrush()->brush.Get(), 
		opacityBrush ? opacityBrush->GetNativeBrush()->brush.Get() : nullptr);
}

void D2DGraphics::DrawText(Platform::String^ string, TextFormat^ fmt, Windows::Foundation::Rect rect, 
						   IBrush^ bgBrush, DRAW_TEXT_OPTIONS options, MEASURING_MODE measuringMode)
{
	if (!string || string->Length() == 0)
		return;
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!fmt)
		throw ref new NullReferenceException();
	if (!bgBrush)
		throw ref new NullReferenceException();

	bgBrush->Context = Context;
	Context->m_d2dContext->DrawText(string->Data(), string->Length()
		, fmt->GetNativeFormat().Get(), DXBase::makeD2D(rect), bgBrush->GetNativeBrush()->brush.Get()
		, static_cast<D2D1_DRAW_TEXT_OPTIONS>(options)
		, static_cast<DWRITE_MEASURING_MODE>(measuringMode)
		);
}

void D2DGraphics::DrawTextLayout(float2 p1, TextLayout^ textLayout, IBrush^ foreground, DRAW_TEXT_OPTIONS options)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!textLayout)
		throw ref new NullReferenceException();
	if (!foreground)
		throw ref new NullReferenceException();

	foreground->Context = Context;
	Context->m_d2dContext->DrawTextLayout(makeD2D(p1), textLayout->mTextLayout.Get(), 
		foreground->GetNativeBrush()->brush.Get(), static_cast<D2D1_DRAW_TEXT_OPTIONS>(options));
}

void D2DGraphics::DrawGlyphRun(float2 baseLineOrigin, GlyphRun^ glyphRun, IBrush ^foreground, MEASURING_MODE measuringMode)
{
	if (!Context->Rendering)
		throw ExHelper::CreateException(ErrorCodes::ContextNotRendering);
	if (!glyphRun)
		throw ref new NullReferenceException();
	if (!foreground)
		throw ref new NullReferenceException();

	foreground->Context = Context;
	Context->m_d2dContext->DrawGlyphRun(makeD2D(baseLineOrigin), glyphRun->GetGlyphRun().get(), 
		foreground->GetNativeBrush()->brush.Get(), static_cast<DWRITE_MEASURING_MODE >(measuringMode));
}
